package com.uit.httt04.duongvanlam.facedetection.algorithm;

import java.util.List;

import com.googlecode.javacv.cpp.opencv_core.CvMat;
import com.uit.httt04.duongvanlam.facedetection.classes.ObjectAlgorithm;

import static com.googlecode.javacv.cpp.opencv_core.*;

import android.annotation.SuppressLint;

@SuppressLint(
{ "DefaultLocale", "UseSparseArrays" })
public class Pca_2d_R extends Algorithm
{

	public Pca_2d_R()
	{
		super();
	}

	public void Train() throws Exception
	{
		if (this.getAmountOfObjects() > 0 && this.getAmountOfFaces() > 0)
		{
			CvMat averageAll = calculateAverageAll(this.getListObjects(), getHeight(), getWidth());
			CvMat G = calculateG(getListObjects(), averageAll, getHeight(), getWidth());

			//
			// <-- EigenVector And EigenValue
			//
			this.setEigenVal(cvCreateMat(getWidth(), 1, CV_32FC1));
			cvZero(this.getEigenVal());
			this.setEigenvect(cvCreateMat(getWidth(), getWidth(), CV_32FC1));
			cvZero(this.getEigenvect());

			cvEigenVV(G, this.getEigenvect(), this.getEigenVal(), 0, -1, -1);
			//
			// EigenVector And EigenValue -->
			//

			//
			// <-- Free
			//
			cvReleaseMat(averageAll);
			cvReleaseMat(G);
			//
			// Free -->
			//
		}
		else
			throw new Exception("Pca cant train because wrong input");
	}

	private CvMat calculateAverageAll(List<ObjectAlgorithm> ListObject, int Height, int Width) throws Exception
	{
		try
		{
			CvMat result = cvCreateMat(Height, Width, CV_32FC1);
			cvSetZero(result);

			for (int i = 0; i < ListObject.size(); i++)
			{
				cvAdd(result, ListObject.get(i).getMatrix(), result, null);
			}

			double coefficient = (double) ((double) 1 / (double) ListObject.size());
			cvConvertScale(result, result, coefficient, 0);

			return result;
		}
		catch (Exception e)
		{
			throw e;
		}
	}

	private CvMat calculateG(List<ObjectAlgorithm> ListObject, CvMat averageAll, int Height, int Width) throws Exception
	{
		try
		{
			CvMat Delta = cvCreateMat(Height, Width, CV_32FC1);
			CvMat DeltaT = cvCreateMat(Width, Height, CV_32FC1);
			// tempMatMultiple = DeltaT * Delta
			CvMat tempMatMultiple = cvCreateMat(Width, Width, CV_32FC1);
			// covariantMatrix = covariantMatrix + tempMatMultiple
			CvMat result = cvCreateMat(Width, Width, CV_32FC1);
			cvSetZero(result);

			for (int i = 0; i < ListObject.size(); i++)
			{
				//
				cvSetZero(Delta);
				cvSetZero(DeltaT);
				cvSetZero(tempMatMultiple);
				//
				cvSub(getListObjects().get(i).getMatrix(), averageAll, Delta, null);
				cvTranspose(Delta, DeltaT);
				cvMatMul(DeltaT, Delta, tempMatMultiple);
				cvAdd(result, tempMatMultiple, result, null);
			}

			double coefficient = (double) ((double) 1 / (double) getListObjects().size());
			cvConvertScale(result, result, coefficient, 0);

			return result;
		}
		catch (Exception e)
		{
			throw e;
		}
	}
}
